"""
Copyright (c) 2022 Huawei Technologies Co.,Ltd.

openGauss is licensed under Mulan PSL v2.
You can use this software according to the terms and conditions of the Mulan PSL v2.
You may obtain a copy of Mulan PSL v2 at:

          http://license.coscl.org.cn/MulanPSL2

THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
See the Mulan PSL v2 for more details.
"""
"""
Case Type   : 硬件故障类--磁盘
Case Name   : vacuum执行过程中，磁盘空间满
Create At   : 2020-10-15
Owner       : n@ningyali
Description :
    1、使用工具DFR-Runner将磁盘即将写满，同时客户端通过jdbc持续写入数据到数据库使磁盘占满
    2、在数据库里执行vacuum命令
    3、消除故障
    4、在数据克重执行vacuum命令
Expect      :
    1、故障注入成功
    2、执行失败
    3、执行故障
    4、执行成功
History     :
    Modified by @wan005:消除故障后重启可能因为主备暂时不一致提示Please check manually
    Modified by wx1115623 2022/10/10:增加重启失败备机重建步骤
"""

import unittest
import time
import os
from yat.test import Node
from yat.test import macro
from testcase.utils.Logger import Logger
from testcase.utils.Common import Common
from testcase.utils.CommonSH import CommonSH
from testcase.utils.Constant import Constant
from testcase.utils.CommonSH import RestartDbCluster


class VacuumAndDiskFull(unittest.TestCase):
    primary_sh = CommonSH('PrimaryDbUser')
    standby1_sh = CommonSH('Standby1DbUser')
    standby2_sh = CommonSH('Standby2DbUser')
    nodes_tuple = ('PrimaryDbUser', 'Standby1DbUser', 'Standby2DbUser')

    @RestartDbCluster(*nodes_tuple)
    def setUp(self):
        self.log = Logger()
        self.log.info(f"-----{os.path.basename(__file__)} start-----")
        self.primary_root_node = Node(node='PrimaryRoot')
        self.primary_user_node = Node(node='PrimaryDbUser')
        self.standby1_root_node = Node(node='Standby1Root')
        self.standby1_user_node = Node(node='Standby1DbUser')
        self.com = Common()
        self.Constant = Constant()
        self.t_name = 'test_hardware010'

        self.log.info("-----获取主节点数据目录所在磁盘-----")
        cmd = f"df -h {macro.DB_INSTANCE_PATH}"
        self.log.info(cmd)
        msg = self.primary_root_node.sh(cmd).result()
        self.log.info(msg)
        self.db_disk_name = msg.splitlines()[-1].split()[0].strip()

    def test_main(self):
        try:
            self.log.info("-----创建table-----")
            create_table_cmd = f'''drop table if exists {self.t_name}; 
                            create table {self.t_name}(id int,
                            name varchar(50),name1 varchar(50),
                            name2 varchar(50));'''
            self.log.info(create_table_cmd)
            msg = self.primary_sh.executDbSql(create_table_cmd)
            self.log.info(msg)
            self.assertIn(self.Constant.CREATE_TABLE_SUCCESS, msg)

            self.log.info("-----注入磁盘满故障-----")
            cfe_command = f'rfile_full (diskname) values ({self.db_disk_name})'
            cfe_msg = self.com.cfe_inject(self.primary_root_node, cfe_command)
            self.log.info(cfe_msg)
            self.assertIn(self.Constant.CFE_DISK_FULL_SUCCESS_MSG, cfe_msg)

            self.log.info("v持续写入数据到数据库使磁盘占满-----")
            for i in range(10000):
                self.log.info(f"-----第{i}次：重复执行DML操作-----")
                insert_cmd = f"insert into {self.t_name} " \
                    f"values (generate_series(1,10000), '测试{i}', " \
                    f"'可靠性测试reliability_hardware0010', '持续写入数据');"
                dml_result = self.primary_sh.executDbSql(insert_cmd)
                self.log.info('sqlexecute result: ' + dml_result)

                self.log.info("-----查看磁盘剩余可用空间-----")
                avail_size = self.com.getAvailSize(self.primary_root_node,
                                                   macro.DB_INSTANCE_PATH)
                self.log.info('Available:' + avail_size)
                if avail_size == '0' or \
                        self.Constant.DISK_FULL_MSG in dml_result:
                    self.log.info('磁盘已满')
                    break

            self.log.info('-----在数据库里执行vacuum命令-----')
            sql_cmd = "vacuum full;"
            self.log.info(sql_cmd)
            msg = self.primary_sh.executDbSql(sql_cmd)
            self.log.info(msg)
            # 磁盘满可能导致数据库主节点stop，继而连接数据库失败
            if self.Constant.FAILED_CONNECT_DB not in msg:
                self.assertIn(self.Constant.DISK_FULL_MSG, msg)
        finally:
            self.log.info('-----消除故障-----')
            cfe_command = f"rfile_full where (diskname={self.db_disk_name})"
            cfe_msg = self.com.cfe_clean(self.primary_root_node, cfe_command)
            self.log.info(cfe_msg)
            self.assertIn(self.Constant.CFE_DISK_CLEAN_SUCCESS_MSG, cfe_msg)

            self.log.info("-----查看磁盘剩余可用空间-----")
            avail_size = self.com.getAvailSize(self.primary_root_node,
                                               macro.DB_INSTANCE_PATH)
            avail_size = avail_size
            self.log.info('Available:' + avail_size)
            self.assertTrue(avail_size != '0')

            self.log.info("-----磁盘满可能导致数据库主节点stop，重启集群以恢复-----")
            time.sleep(60)
            stop_msg = self.primary_sh.stopDbCluster()
            self.assertTrue(stop_msg)
            is_started = self.primary_sh.startDbCluster('get_detail')
            flg = self.Constant.START_SUCCESS_MSG in is_started or \
                  "Please check manually" in is_started
            self.assertTrue(flg)
            if "Please check manually" in is_started:
                result = self.standby1_sh.buildStandby()
                self.assertIn(self.Constant.BUILD_SUCCESS_MSG, result)
                result = self.standby2_sh.buildStandby()
                self.assertIn(self.Constant.BUILD_SUCCESS_MSG, result)
            self.assertTrue(self.primary_sh.getDbClusterStatus('status'))

            self.log.info('-----在数据库里再次执行vacuum命令-----')
            sql_cmd = "vacuum full;"
            self.log.info(sql_cmd)
            msg = self.primary_sh.executDbSql(sql_cmd)
            self.log.info(msg)
            self.assertIn('VACUUM', msg)

    def tearDown(self):
        self.log.info("-----清除故障-----")
        cfe_command = f"rfile_full where (diskname={self.db_disk_name})"
        cfe_msg = self.com.cfe_clean(self.primary_root_node, cfe_command)
        self.log.info(cfe_msg)
        self.assertIn(self.Constant.CFE_DISK_CLEAN_SUCCESS_MSG, cfe_msg)

        self.log.info('-----清理环境，删除表-----')
        drop_table_cmd = f'''drop table if exists {self.t_name}; '''
        self.log.info(drop_table_cmd)
        msg = self.primary_sh.executDbSql(drop_table_cmd)
        self.log.info(msg)
        self.log.info(f"-----{os.path.basename(__file__)} end-----")
